import math
import os
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import function
import random
import do_pic


class Sparrow:
    def __init__(self, d, p, i, S_T, P, S_D, d_min, d_max, n_t, fu, pic, experiment):
        self.dim = d  # 维度
        self.pq = p  # 总群数量
        self.iter_num = i  # 迭代次数
        self.ST = S_T  # 发现者警戒阈值
        self.PR = P  # 发现者比例
        self.SD = S_D  # 侦察者比例
        self.data_range_max = d_max  # 取值范围
        self.data_range_min = d_min  # 取值范围
        self.ty = n_t  # 最佳函数是最大还是最小值
        # ty=1:求最大值  ty=0求最小值
        self.experiment = experiment
        self.num_PR = int(self.pq * self.PR)  # 发现者麻雀群落中单体个数
        self.num_FL = int(self.pq * (1 - self.PR))  # 跟随者麻雀群落中单体个数
        self.num_SD = int(self.pq * self.SD)  # 侦查者麻雀群落中单体个数
        self.pic = pic  # 是否看图
        self.best_x = []
        self.best_function = 0
        self.worst_x = []
        self.worst_function = 0
        self.fu = fu  # 函数类型
        self.SSA_pq = np.random.uniform(self.data_range_min, self.data_range_max, (self.pq, self.dim))  # 创建初始群落
        self.SSA_fuaction = np.random.randn(p)

    def jubge_borde(self, Xid_t):

        if Xid_t >= self.data_range_max:
            Xid_t = self.data_range_max
        else:
            pass

        if Xid_t <= self.data_range_min:
            Xid_t = self.data_range_min
        else:
            pass
        return Xid_t

    def Predator_position(self, loc, Xij_t, R2):  # 发现者麻雀：第local只的位置更新
        """
        :param loc: 第loc只麻雀
        :param Xid_t: 这只麻雀所在的位置
        :return: Xid_t1 更新这只麻雀所在的位置
        """
        a = np.random.rand()
        Q = np.random.randn()
        for i in range(self.dim):
            if R2 < self.ST:
                Xij_t[i] = Xij_t[i] * math.exp((-loc) / (a * self.iter_num))
                Xij_t[i] = self.jubge_borde(Xij_t[i])
            else:
                Xij_t[i] = Xij_t[i] + Q
                Xij_t[i] = self.jubge_borde(Xij_t[i])
        return Xij_t

    def Subscriber_position(self, loc, Xij_t, X_best, X_Gl_worst):  # 跟随着麻雀：第local只的位置更新
        Q = np.random.randn()
        X_best = X_best[0]

        for i in range(self.dim):
            A = [-1, 1]
            if loc > self.pq / 2:
                Xij_t[i] = Q * math.exp((X_Gl_worst[i] - Xij_t[i]) / (loc ** 2))
                Xij_t[i] = self.jubge_borde(Xij_t[i])

            else:
                f = 0
                for n in range(self.dim):
                    f = f + (random.choice(A) * abs(Xij_t[i] - X_best[i]))

                Xij_t[i] = X_best[i] + (1 / self.dim) * f

                Xij_t[i] = self.jubge_borde(Xij_t[i])
            return Xij_t

    def Lookout_position(self, loc, Xij_t, X_best, X_worst, fi, fg, fw):

        B = np.random.randn()
        k = np.random.uniform(-1, 1)
        for i in range(self.dim):
            if self.ty == 1:  # 求最大值
                if self.SSA_fuaction[loc] < fg:
                    Xij_t[i] = Xij_t[i] + B * abs(Xij_t[i] - X_best[i])
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
                elif self.SSA_fuaction[loc] == fg:
                    Xij_t[i] = Xij_t[i] + k * (abs(Xij_t[i] - X_worst[i]) / (fi - fw + float("inf")))
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
                else:
                    Xij_t[i] = Xij_t[i] + B * abs(Xij_t[i] - X_best[i])
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
            else:  # 求最小值
                if self.SSA_fuaction[loc] > fg:
                    Xij_t[i] = Xij_t[i] + B * abs(Xij_t[i] - X_best[i])
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
                elif self.SSA_fuaction[loc] == fg:
                    Xij_t[i] = Xij_t[i] + k * (abs(Xij_t[i] - X_worst[i]) / (fi - fw + float("inf")))
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
                else:
                    Xij_t[i] = Xij_t[i] + B * abs(Xij_t[i] - X_best[i])
                    Xij_t[i] = self.jubge_borde(Xij_t[i])
        return Xij_t

    # def create_A_xin(self):
    #     l = np.ones(self.dim)
    #     l = np.matrix(l)
    #     # l是1*d的单位矩阵
    #     A = np.floor(np.random.rand(1, 2) * 2) * 2 - 1
    #     A = np.matrix(A)
    #     # A是1*d的[-1，1]的随机数矩阵
    #     B = np.dot(A.T, np.dot(A, A.T).I)
    #     result = np.dot(B, l)
    #     return result

    def calculate_best(self, num):
        if self.ty == 1:
            f_best = max(self.SSA_fuaction[0:num])
            f_worst = min(self.SSA_fuaction[0:num])

            X_best_index = np.where(self.SSA_fuaction[0:num] == f_best)
            X_best = self.SSA_pq[X_best_index]

            X_worst_index = np.where(self.SSA_fuaction[0:num] == f_worst)
            X_worst = self.SSA_pq[X_worst_index]
        else:
            f_best = min(self.SSA_fuaction[0:num])
            f_worst = max(self.SSA_fuaction[0:num])

            X_best_index = np.where(self.SSA_fuaction[0:num] == f_best)
            X_best = self.SSA_pq[X_best_index]

            X_worst_index = np.where(self.SSA_fuaction[0:num] == f_worst)
            X_worst = self.SSA_pq[X_worst_index]
        return f_best, f_worst, X_best, X_worst

    def re_ad_fu(self):
        temporary_f = np.ones(self.pq)
        temporary_x = self.SSA_pq
        for i in range(self.pq):
            if self.ty == 1:
                max = np.sort(self.SSA_fuaction)[-i]
                max_index = np.argsort(self.SSA_fuaction)[-i]
                temporary_f[i] = max
                temporary_x[i] = self.SSA_pq[max_index]
            else:
                max = np.sort(self.SSA_fuaction)[i]
                max_index = np.argsort(self.SSA_fuaction)[i]
                temporary_f[i] = max
                temporary_x[i] = self.SSA_pq[max_index]
        return temporary_f, temporary_x

    def SSA_run(self):

        for i in range(self.pq):  # 初始化函数适应值
            self.SSA_fuaction[i] = function.choose(self.fu, self.dim, self.SSA_pq[i])

        self.SSA_fuaction, self.SSA_pq = self.re_ad_fu()  # 分割发现者与跟随者

        GL_f_best, GL_f_worst, GL_X_best, GL_X_worst = self.SSA_fuaction[0], self.SSA_fuaction[self.pq - 1], \
                                                       self.SSA_pq[0], self.SSA_pq[self.pq - 1]  # 最佳位置麻雀与最差位置麻雀，\
        # 包括其适应值
        G_X = GL_X_best  # 设置全局最佳最差位置与适应值
        G_F = GL_f_best
        G_WF = GL_f_worst
        G_WX = GL_X_worst
        xc = self.SSA_pq
        fc = self.SSA_fuaction

        if self.pic == 1:
            do_pic.do_pic(self.SSA_pq, self.data_range_min, self.data_range_max, self.num_PR, G_X)
            sd = 'The_' + self.experiment + '_test____' + self.fu + '____The_' + '-1' + '_iter_loop'
            plt.title(self.experiment + ',' + '-1', y=1, loc='left')
            plt.xlabel("global_x=" + str(G_X) + '__' + "global_y=" + str(round(G_F,5)))
            plt.title(sd)
            path = 'pic/pic_' + self.fu + '/experiment_index' + self.experiment
            isExists = os.path.exists(path)
            if not isExists:
                os.makedirs(path)
                print(path + ' 创建成功')
            else:
                pass
            plt.savefig(path + '/' + sd + '.jpg')
            # plt.draw()
            # plt.pause(1)
            plt.clf()


        for iter in range(self.iter_num):
            print(iter)
            #######################################################################发现者麻雀
            for i in range(0, self.num_PR):
                R2 = np.random.rand(1)
                self.SSA_pq[i] = self.Predator_position(i + 1, self.SSA_pq[i], R2)  # 更新发现者位置
                self.SSA_fuaction[i] = function.choose(self.fu, self.dim, self.SSA_pq[i])  # 更新每只麻雀的函数适应值
            PR_f_best, PR_f_worst, PR_X_best, PR_X_worst = self.calculate_best(self.num_PR)
            #######################################################################发现者麻雀

            #######################################################################跟随着麻雀
            for n in range(self.num_PR, self.num_PR + self.num_FL):
                self.SSA_pq[n] = self.Subscriber_position(self.num_PR, self.SSA_pq[n], PR_X_best, GL_X_worst)
                self.SSA_fuaction[n] = function.choose(self.fu, self.dim, self.SSA_pq[n])  # 更新每只麻雀的函数适应值
            #######################################################################跟随着麻雀

            #######################################################################侦察者麻雀
            c = random.sample(range(0, self.pq), self.num_SD)  # 这个的作用是在种群中随机产生其位置（也就是这部分的麻雀位置一开始是随机的，意识到危险了要进行位置移动，
            for m in c:
                self.SSA_pq[m] = self.Lookout_position(m, self.SSA_pq[m], G_X, G_WX, self.SSA_fuaction[m],
                                                       G_F, G_WF)
                self.SSA_fuaction[m] = function.choose(self.fu, self.dim, self.SSA_pq[m])  # 更新每只麻雀的函数适应值
            #######################################################################侦察者麻雀

            self.SSA_fuaction, self.SSA_pq = self.re_ad_fu()  # 重新分割发现者与跟随者

            """
            接下来是画出每一次发现者与跟随者的位置，动态更新方式
            """

            if self.pic == 1:
                do_pic.do_pic(self.SSA_pq, self.data_range_min, self.data_range_max, self.num_PR, G_X)
                sd = 'The_' + self.experiment + '_test____' + self.fu + '____The_' + str(iter) + '_iter_loop'
                plt.xlabel("global_x=" + str(G_X) + '__' + "global_y=" + str(round(G_F,5)))
                plt.title(self.experiment + ',' + str(iter), y=1, loc='left')

                plt.title(sd)
                path = 'pic/pic_' + self.fu + '/experiment_index' + self.experiment
                isExists = os.path.exists(path)
                if not isExists:
                    os.makedirs(path)
                    print(path + ' 创建成功')
                else:
                    pass
                plt.savefig(path + '/' + sd + '.jpg')
                plt.draw()
                plt.pause(0.1)
                plt.clf()
            # 产生本次最佳最差位置与适应值
            GL_f_best, GL_f_worst, GL_X_best, GL_X_worst = self.SSA_fuaction[0], self.SSA_fuaction[self.pq - 1], \
                                                           self.SSA_pq[0], self.SSA_pq[self.pq - 1]
            # 更新全局最佳最差位置与适应值
            if self.ty == 0:
                G_F = min(G_F, GL_f_best)
                if G_F == GL_f_best:
                    G_X = GL_X_best
                G_WF = max(G_WF, GL_f_worst)
                if G_WF == GL_f_worst:
                    G_WX = GL_X_worst
            else:
                G_F = max(G_F, GL_f_best)
                if G_F == GL_f_best:
                    G_X = GL_X_best
                G_WF = min(G_WF, GL_f_worst)
                if G_WF == GL_f_worst:
                    G_WX = GL_X_worst

            xc = np.append(xc, self.SSA_pq, axis=0)

            fc = np.append(fc, self.SSA_fuaction, axis=0)
        temp = []
        for i in range(len(xc)):
            temp.append(str(xc[i]))
        dict = {'X_best': temp, 'F_best': fc}
        path = 'cs/' + str(self.fu)
        isExists = os.path.exists(path)
        if not isExists:
            os.makedirs(path)
            print(path + ' 创建成功')
        else:
            pass
        df = pd.DataFrame(dict)
        df.to_csv(path + '/' + str(self.experiment) + '.csv')
        return G_X, G_F
